<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <!-- 自定义组件学习 标题编号1、2、.... ctrl+F查找即可-->

  <!-- 1、介绍 -->

  <!-- 注意点： -->

  <!--首先需要在 json 文件中进行自定义组件声明
         {
            "component": true
         } -->

  <!-- 在组件wxss中不应使用ID选择器、属性选择器和标签名选择器。 -->

  <!-- 在自定义组件的 js 文件中，需要使用 Component() 来注册组件，并提供组件的属性定义、内部数据和自定义方法。 -->

  <!-- 使用已注册的自定义组件前，首先要在页面的 json 文件中进行引用声明。 
    {
        "usingComponents": {
        "component-tag-name": "path/to/the/custom/component"
    }
}
    -->

  <!-- 开发者工具 1.02.1810190 及以上版本支持在 app.json 中声明 usingComponents 字段，在此处声明的自定义组件视为全局自定义组件，在小程序内的页面或自定义组件中可以直接使用而无需再声明。 -->

  <!-- 注意事项
    一些需要注意的细节：

    因为 WXML 节点标签名只能是小写字母、中划线和下划线的组合，所以自定义组件的标签名也只能包含这些字符。
    自定义组件也是可以引用自定义组件的，引用方法类似于页面引用自定义组件的方式（使用 usingComponents 字段）。
    自定义组件和页面所在项目根目录名不能以“wx-”为前缀，否则会报错。 -->

  <!-- 实操案例： -->
  <!-- {
        "usingComponents": {
            "mycomponents": "/components/myComponents/MyComponents"
         }
    } -->
  <!-- <  mycomponents newName="hhhh"></> -->
  <!-- // components/myComponents/MyComponents.js
    Component({
    
      /**
       * 组件的属性列表
       */
      properties: {
        newName:{
          type:String,
          value:"haha"
        }
      },
    
      /**
       * 组件的初始数据
       */
      data: {
        name:"fangdaochu"
      },
    
      /**
       * 组件的方法列表
       */
      methods: {
    
      }
    }) -->

  <!-- 2、组件模板和样式 -->

  <!-- 插槽： -->
  <!-- 在组件模板中可以提供一个 <slot> 节点，用于承载组件引用时提供的子节点。 -->
  <!-- 如json内未定义自定义模板 则slot会被当作一个无意义的节点 -->
  <!-- 例子 -->
  <!-- <view class="MyComponents-container">
        <view>下面是插槽的使用</view>
        <slot></slot>
    </view> -->
  <!-- <mycomponents>
        //下面代码 会被插入到组件的插槽
        <view>hahah</view>
    </mycomponents> -->

  <!-- 模板数据绑定 -->
  <!-- 可以使用数据绑定，这样就可以向子组件的属性传递动态数据 -->
  <!-- 例子： -->
  <!-- <view class="MyComponents-container">
        {{test}}
    </view> -->
  <!-- properties: {
        // newName:{
        //   type:String,
        //   value:"haha"
        // },
        test:{
          type:String,
        }
      }, -->
  <!-- <mycomponents test="{{test}}"></mycomponents> -->
  <!-- test:"hello World" -->
  <!-- 在以上例子中，组件的属性test将收到页面传递的数据。页面可以通过 setData 来改变绑定的数据字段。 -->

  <!-- 多插槽情况 默认情况下，一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时，可以在组件 js 中声明启用。以不同的 name 来区分-->
  <!-- Component({
        options: {
          multipleSlots: true // 在组件定义时的选项中启用多slot支持
        },
        properties: { /* ... */ },
        methods: { /* ... */ }
      }) -->
  <!-- 使用时，用 slot 属性来将节点插入到不同的 slot 上。 -->
  <!--例子： -->
  <!-- <view class="MyComponents-container">
        <slot name="one"></slot>
        <slot></slot>
        <slot name="two"></slot>
    </view> -->
  <!-- /**
    * 组件的配置
    */
     options: {
       multipleSlots: true
     } -->
  <!-- <mycomponents>
        <view slot="two">这个被插入到名字为two的插槽</view>
        <view>这个被插入到默认插槽中</view>
        <view slot="one">这个被插入到名字为one的插槽</view>
    </mycomponents> -->

  <!-- 组件样式 -->
  <!-- 继承样式，如 font 、 color ，会从组件外继承到组件内。
    除继承样式外， app.wxss 中的样式、组件所在页面的的样式对自定义组件无效（除非更改组件样式隔离选项）。 -->
  <!-- #a { } /* 在组件中不能使用 */
    [a] { } /* 在组件中不能使用 */
    button { } /* 在组件中不能使用 */
    .a > .b { } /* 除非 .a 是 view 组件节点，否则不一定会生效 */ -->

  <!-- 组件可以指定它所在节点的默认样式，使用 :host 选择器 -->
  <!-- 例子： -->
  <!-- <view class="MyComponents-container">
        <view class="view-container">:host设置节点默认样式实例</view>
        <slot></slot>
    </view> -->
  <!-- :host {
        color: red;
    } -->
  <!-- <mycomponents>
        <view>:host节点是什么？</view>
    </mycomponents> -->

  <!-- 组件样式隔离 定义在Components构造器的options配置中-->
  <!-- 默认情况下，自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况：
    指定特殊的样式隔离选项 styleIsolation 。
    webview 渲染下，在 app.wxss 或页面的 wxss 中使用标签名选择器（或一些其他特殊选择器）来直接指定样式会影响到页面和全部组件。通常情况下这是不推荐的做法。
    {
        "styleIsolation": "isolated"
    } -->
  <!-- 例子： -->
  <!-- <view class="MyComponents-container">
        <view class="view-container">页面的class会对我有影响吗？</view>
    </view> -->
  <!-- .view-container{
        color: red;
    } -->
  <!-- // 使用后两者时，请务必注意组件间样式的相互影响。
    // styleIsolation: 'isolated',//外部页面和内部组件相互不影响
    // styleIsolation: 'apply-shared',//外部页面wxcc会影响内部组件，内部组件不影响外部
    // styleIsolation: "shared"//外部页面wxcc会影响内部组件，内部组件也影响外部 -->
  <!-- 页面：
    <mycomponents></mycomponents>
    <view class="view-container">hahah</view> -->
  <!-- .view-container{
        color: yellow;
    } -->

  <!-- 外部样式类 -->
  <!-- 有时，组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义段定义若干个外部样式类。 -->
  <!-- 例子： -->
  <!-- <view class="MyComponents-container">
        <view class="my-class">我的颜色是什么？</view>
    </view> -->
  <!-- /**
    * 组件外部类
    */
   externalClasses: ['my-class'] -->

  <!-- <mycomponents my-class="red-text"></mycomponents> -->
  <!-- .red-text{
        color:red
    } -->

  <!-- 引用页面或父组件的样式 ~|^来局部引用  -->
  <!-- 但是 如果组件是比较独立、通用的组件，请优先使用外部样式类的方式，而非直接引用父组件或页面的样式。 -->
  <!-- <view class="MyComponents-container">
        <view class="~red-text">我的颜色是什么？</view>
    </view> -->
  <!-- <mycomponents></mycomponents> -->
  <!-- .red-text{
        color:red
    } -->

  <!-- 虚拟化组件节点 -->
  <!-- 即默认情况下，自定义组件本身的那个节点是一个“普通”的节点，使用时可以在这个节点上设置 class style 、动画、 flex 布局等，就如同普通的 view 组件节点一样。 -->
  <!-- 但有些时候，自定义组件并不希望这个节点本身可以设置样式、响应 flex 布局等，而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定。 -->
  <!-- options: {
        virtualHost: true
    }, -->
  <!-- 需要注意的是，自定义组件节点上的 class style 和动画将不再生效，但仍可以：
    将 style 定义成 properties 属性来获取 style 上设置的值；
    将 class 定义成 externalClasses 外部样式类使得自定义组件 wxml 可以使用 class 值。 -->
  <!-- 例子： -->
  <!-- 没设置虚拟组件前：
    组件：<view>我不是虚拟组件</view>
    页面：<mycomponents style="color: red; border: 1rpx solid;width: 200rpx;height: 200rpx;display: block;"></mycomponents>
    以上页面可以通过style class进行修改 那么可以通过虚拟组件的方法解决这个问题
    -->
  <!-- 虚拟组件：
    <view>
	    <view style="{{style}}" class="class">我是虚拟组件了</view>
    </view>
    // components/myComponents/MyComponents.js
    Component({
    /**
    * 组件的属性列表
    */
    properties: {
        style:{
        type:String
        }
    },
    /**
    * 组件的配置
    */
    options: {
        virtualHost:true//虚拟化组件节点 使自定义组件内部的第一层节点由自定义组件本身完全决定
    },
    /**
    * 组件外部类
    */
    externalClasses: ['class']
    })
    页面：<mycomponents style="color: red; border: 1rpx solid;width: 200rpx;height: 200rpx;display: block;" class="test"></mycomponents>
    .test{
	    text-align: center;
	    line-height: 100rpx;
    }
    -->

  <!-- 3、Component 构造器 -->
  <!-- Component 构造器可用于定义组件，调用 Component 构造器时可以指定组件的属性、数据、方法等。 -->
  <!-- 模板：
    Component({

    behaviors: [],//类似于vue mixins 提取公共代码

    properties: {//外部传入参数
        myProperty: { // 属性名
        type: String,
        value: ''
        },
        myProperty2: String // 简化的定义方式
    },
    
    data: {}, // 私有数据，可用于模板渲染

    lifetimes: {// 生命周期函数，可以为函数，或一个在methods段中定义的方法名
        attached: function () { },
        moved: function () { },
        detached: function () { },
    },

    // 生命周期函数，可以为函数，或一个在methods段中定义的方法名 推荐写在lifetimes内
    attached: function () { }, // 此处attached的声明会被lifetimes字段中的声明覆盖
    ready: function() { },

    pageLifetimes: {// 组件所在页面的生命周期函数
        show: function () { },
        hide: function () { },
        resize: function () { },
    },

    methods: {
        onMyButtonTap: function(){
        this.setData({
            // 更新属性和数据的方法与更新页面数据的方法类似
        })
        },
        // 内部方法建议以下划线开头
        _myPrivateMethod: function(){
        // 这里将 data.A[0].B 设为 'myPrivateData'
        this.setData({
            'A[0].B': 'myPrivateData'
        })
        },
        _propertyChange: function(newVal, oldVal) {
        }
    }
    })
    -->

  <!-- 使用 Component 构造器构造页面 -->
  <!-- 优点：使用 Component 构造器来构造页面的一个好处是可以使用 behaviors 来提取所有页面中公用的代码段。 -->
  <!-- 注意点：
        此时要求对应 json 文件中包含 usingComponents 定义段。
        页面的生命周期方法（即 on 开头的方法），应写在 methods 定义段中。
    -->
  <!-- 例子： -->
  <!-- <button bind:tap="handleTap">点击</button> -->
  <!-- handleTap() {
        wx.navigateTo({
          url: '/pages/test/test?numA=123&numB=aaaa',//给页面传递参数
        })
      }, -->
  <!-- <view>
        用Components来构建页面
        {{numA}}
        {{numB}}
    </view> -->
  <!-- // pages/test/test.js
    Component({
    
      properties: {
        numA: {
          type: String
        },
        numB: {
          type: String
        }
      },
      methods:{
        onLoad:function(e){//也可以获得参数 且生命周期函数定义在methods内
          console.log("onload",e)
        }
      }
    }) -->
  <!-- 暂不演示behaviors代码  -->

  <!-- 4、组件间通信与事件 -->

  <!-- 组件间的基本通信方式有以下几种。
    WXML 数据绑定：用于父组件向子组件的指定属性设置数据，仅能设置 JSON 兼容数据（自基础库版本 2.0.9 开始，还可以在数据中包含函数）。具体在 组件模板和样式 章节中介绍。
    事件：用于子组件向父组件传递数据，可以传递任意数据。
    如果以上两种方式不足以满足需要，父组件还可以通过 this.selectComponent 方法获取子组件实例对象，这样就可以直接访问组件的任意数据和方法。
    -->

  <!-- 监听事件 即监听自定义组件的事件 和监听基础组件事件的方法完全一致 -->
  <!-- 示例： -->
  <!-- 组件：
    <view>
	    我是自定义组件 我有一个自定义事件，事件名为：myEvent
    </view>
     -->
  <!-- 页面：
    <mycomponents bind:myEvent="handleMyEvent"></mycomponents>
    handleMyEvent(){
        console.log("handleMyEvent触发咯")
    }, -->

  <!-- 触发事件 -->
  <!-- 自定义组件触发事件时，需要使用 triggerEvent 方法，指定事件名、detail对象和事件选项： -->
  <!-- 组件：
    <view>
	    我是自定义组件 我有一个自定义事件，事件名为：myEvent
	    <button bind:tap="handleEvent">点击触发自定义事件</button>
    </view>
    methods: {
        handleEvent(){
            var myEventDetail = {} // detail对象，提供给事件监听函数 参数
            var myEventOption = {} // 触发事件的选项
            this.triggerEvent('myEvent', myEventDetail, myEventOption)
        }
    },

    触发事件的选项包括：
    选项名	类型	是否必填	默认值	描述
    bubbles	Boolean	否	false	事件是否冒泡
    composed	Boolean	否	false	事件是否可以穿越组件边界，为false时，事件将只能在引用组件的节点树上触发，不进入其他任何组件内部
    capturePhase	Boolean	否	false	事件是否拥有捕获阶段 
    例子参考：https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
    -->

  <!-- 获取组件实例     -->
  <!-- 可在父组件里调用 this.selectComponent ，获取子组件的实例对象。 类似于vue的ref $refs -->
  <!-- 用class类查询 -->
  <!-- 例子： -->
  <!-- 组件：<view>我是组件</view> -->
  <!-- 页面：
    <mycomponents id="my-component"></mycomponents>
    <button bind:tap="handleTap">Tap</button>
    handleTap(){
        console.log(this.selectComponent('#my-component'))
      }, -->
  <!-- 在上例中，父组件将会获取 class 为 my-component 的子组件实例对象，即子组件的 this 。 -->

  <!-- 自定义组件 可以定义this.selectComponent返回值 即通过微信内置behaviors: ['wx://component-export'], 
    在设置export(){
        //...
    }使用该 behavior 时，自定义组件中的 export 定义段将用于指定组件被 selectComponent 调用时的返回值
    -->
  <!-- 例子： -->
  <!-- 组件：<view>自定义this.selectComponent返回值</view>
    behaviors:['wx://component-export'],
    export(){
        return {mySet:"mySet"}
    },
    -->
  <!-- 页面：
    <mycomponents id="my-component"></mycomponents>
    <button bind:tap="handleTap">Tap</button>
    handleTap(){
        console.log(this.selectComponent('#my-component'))//{mySet: "mySet"}
      }, -->

  <!-- 5、组件生命周期 -->
  <!-- 其中，最重要的生命周期是 created attached detached ，包含一个组件实例生命流程的最主要时间点。 -->
  <!-- 在组件完全初始化完毕、进入页面节点树后， attached 生命周期被触发。此时， this.data 已被初始化为组件的当前值。这个生命周期很有用，绝大多数初始化工作可以在这个时机进行。 -->
  <!-- 组件的的生命周期也可以在 lifetimes 字段内进行声明（这是推荐的方式，其优先级最高）。 -->
  <!-- 模板：
      Component({
        lifetimes: {
          attached: function() {
            // 在组件实例进入页面节点树时执行
          },
          detached: function() {
            // 在组件实例被从页面节点树移除时执行
          },
        },
        // 以下是旧式的定义方式，可以保持对 <2.2.3 版本基础库的兼容 或被lifetimes覆盖
        attached: function() {
          // 在组件实例进入页面节点树时执行
        },
        detached: function() {
          // 在组件实例被从页面节点树移除时执行
        },
        // ...
      }) -->

  <!-- 可用的全部生命周期如下表所示。
    生命周期 参数 描述 最低版本
    created 无 在组件实例刚刚被创建时执行 1.6.3
    attached 无 在组件实例进入页面节点树时执行 1.6.3
    ready 无 在组件在视图层布局完成后执行 1.6.3
    moved 无 在组件实例被移动到节点树另一个位置时执行 1.6.3
    detached 无 在组件实例被从页面节点树移除时执行 1.6.3
    error Object Error 每当组件方法抛出错误时执行 2.4.1
     -->

  <!-- 组件所在页面的生命周期 -->
  <!-- 生命周期 参数 描述 最低版本
    show 无 组件所在的页面被展示时执行 2.2.3
    hide 无 组件所在的页面被隐藏时执行 2.2.3
    resize Object Size 组件所在的页面尺寸变化时执行 2.4.0
    routeDone 无 组件所在页面路由动画完成时执行 2.31.2 -->
  <!-- 模板：
    Component({
        pageLifetimes: {
          show: function() {
            // 页面被展示
          },
          hide: function() {
            // 页面被隐藏
          },
          resize: function(size) {
            // 页面尺寸变化
          }
        }
      }) -->

  <!-- 例子： -->
  <!-- <view>自定义组件来学习生命周期函数</view> -->
  <!--
    /**
    * 生命周期函数
    */ 
    lifetimes:{
        created(){
          console.log("created")
        },
        attached(){
          console.log("attached")
        }
        //.... move detached error
      },
      pageLifetimes:{
        show(){
          console.log("show")
        },
        hide(){
          console.log("hide")
        } -->
  <!-- 注意：自定义 tabBar 的 pageLifetime 不会触发。 -->

  <!-- 6、behaviors  -->
  <!-- behaviors 是用于组件间代码共享的特性，类似于一些编程语言中的 “mixins” 或 “traits”。 -->
  <!-- 每个 behavior 可以包含一组属性、数据、生命周期函数和方法。组件引用它时，它的属性、数据和方法会被合并到组件中，生命周期函数也会在对应时机被调用。 每个组件可以引用多个 behavior ，behavior 也可以引用其它 behavior  -->

  <!-- 组件中使用 组件引用时，在 behaviors 定义段中将它们逐个列出即可。 -->
  <!-- 模板：
    var myBehavior = require('my-behavior')
    Component{
         behaviors: [myBehavior],
    }
     -->

  <!-- 同名字段的覆盖和组合规则 -->
  <!-- 组件和它引用的 behavior 中可以包含同名的字段，对这些字段的处理方法如下：
    ···如果有同名的属性 (properties) 或方法 (methods)：
        若组件本身有这个属性或方法，则组件的属性或方法会覆盖 behavior 中的同名属性或方法；
        若组件本身无这个属性或方法，则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法；
        在 2 的基础上，若存在嵌套引用 behavior 的情况，则规则为：引用者 behavior 覆盖 被引用的 behavior 中的同名属性或方法。
    ···如果有同名的数据字段 (data)：
        若同名的数据字段都是对象类型，会进行对象合并；
        其余情况会进行数据覆盖，覆盖规则为： 引用者 behavior > 被引用的 behavior 、 靠后的 behavior > 靠前的 behavior。（优先级高的覆盖优先级低的，最大的为优先级最高）
    ···生命周期函数和 observers 不会相互覆盖，而是在对应触发时机被逐个调用：
        对于不同的生命周期函数之间，遵循组件生命周期函数的执行顺序；
        对于同种生命周期函数和同字段 observers ，遵循如下规则：
        behavior 优先于组件执行；
        被引用的 behavior 优先于 引用者 behavior 执行；
        靠前的 behavior 优先于 靠后的 behavior 执行；
        如果同一个 behavior 被一个组件多次引用，它定义的生命周期函数和 observers 不会重复执行。
     -->

  <!-- 内置 behaviors -->
  <!-- 自定义组件可以通过引用内置的 behavior 来获得内置组件的一些行为 -->
  <!-- 之前说了一个自定义组件this.selectComponent返回值的内置对象wx://component-export -->

  <!-- 7、组件间关系 -->
  <!-- 定义和使用组件间关系 -->
  <!-- 例子：
    <custom-ul>
        <custom-li> item 1 </custom-li>
        <custom-li> item 2 </custom-li>
    </custom-ul> -->
  <!-- 上述两个自定义组件关系 复杂 通过学习的通信方式（{{数值}}、事件、this.selectComponent）都不好完成数据的通信  -->
  <!-- 此时在组件定义时加入 relations 定义段，可以解决这样的问题 -->

  <!-- 且：注意：必须在两个组件定义中都加入relations定义，否则不会生效。 -->

  <!-- relations 定义段
    relations 定义段包含目标组件路径及其对应选项，可包含的选项见下表。
    选项 类型 是否必填 描述
    type String 是 目标组件的相对关系，可选的值为 parent 、 child 、 ancestor 、 descendant
    linked Function 否 关系生命周期函数，当关系被建立在页面节点树中时触发，触发时机在组件attached生命周期之后
    linkChanged Function 否 关系生命周期函数，当关系在页面节点树中发生改变时触发，触发时机在组件moved生命周期之后
    unlinked Function 否 关系生命周期函数，当关系脱离页面节点树时触发，触发时机在组件detached生命周期之后
    target String 否 如果这一项被设置，则它表示关联的目标节点所应具有的behavior，所有拥有这一behavior的组件节点都会被关联 -->
  <!-- 模板：
    // path/to/custom-ul.js
    Component({
    relations: {
        './custom-li': {
        type: 'child', // 关联的目标节点应为子节点
        linked: function(target) {
            // 每次有custom-li被插入时执行，target是该节点实例对象，触发在该节点attached生命周期之后
        },
        linkChanged: function(target) {
            // 每次有custom-li被移动后执行，target是该节点实例对象，触发在该节点moved生命周期之后
        },
        unlinked: function(target) {
            // 每次有custom-li被移除时执行，target是该节点实例对象，触发在该节点detached生命周期之后
        }
        }
    },
    methods: {
        _getAllLi: function(){
        // 使用getRelationNodes可以获得nodes数组，包含所有已关联的custom-li，且是有序的
        var nodes = this.getRelationNodes('path/to/custom-li')
        }
    },
    ready: function(){
        this._getAllLi()
    }
    })
    
    // path/to/custom-li.js
    Component({
    relations: {
        './custom-ul': {
        type: 'parent', // 关联的目标节点应为父节点
        linked: function(target) {
            // 每次被插入到custom-ul时执行，target是custom-ul节点实例对象，触发在attached生命周期之后
        },
        linkChanged: function(target) {
            // 每次被移动后执行，target是custom-ul节点实例对象，触发在moved生命周期之后
        },
        unlinked: function(target) {
            // 每次被移除时执行，target是custom-ul节点实例对象，触发在detached生命周期之后
        }
        }
    }
    })
    -->

  <!-- 通过this.getRelationNodes('path/to/custom-li')可以获得所有已关联的节点，且是有序的。 -->

  <!-- 关联一类组件 -->
  <!-- 即两个组件有共同的一个behavior，祖先组件在则在 relations 关系定义中，可使用这个behavior来代替组件路径作为关联的目标节点： -->
  <!-- 模板：// path/to/custom-form.js
    var customFormControls = require('./custom-form-controls')
    Component({
      relations: {
        'customFormControls': {
          type: 'descendant', // 关联的目标节点应为子孙节点
          target: customFormControls
          linked: function(target) {
            console.info('已关联到 ' + target.is)
        }
        }
      }
    }) -->
  <!-- 子组件：// path/to/custom-input.js
    var customFormControls = require('./custom-form-controls')
    Component({
    behaviors: [customFormControls],
    relations: {
        './custom-form': {
        type: 'ancestor', // 关联的目标节点应为祖先节点
        }
    }
    }) -->

  <!-- 8、数据监听器 observers 类似于vue中的watch -->
  <!-- 数据监听器可以用于监听和响应任何属性和数据字段的变化。 -->

  <!-- 简单案例模板：
    Component({
        attached: function() {
            this.setData({
            numberA: 1,
            numberB: 2,
            })
        },
        observers: {
            'numberA, numberB': function(numberA, numberB) {
            // 在 numberA 或者 numberB 被设置时，执行这个函数
            this.setData({
                sum: numberA + numberB
            })
            }
        }
    })
    -->

  <!-- 数据监听器支持监听属性或内部数据的变化，可以同时监听多个。一次 setData 最多触发每个监听器一次。
    同时，监听器可以监听子数据字段 -->

  <!-- 如果需要监听所有子数据字段的变化，可以使用通配符 **  -->
  <!-- 模板：observers: {
    'some.field.**': function(field) {
      // 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发
      // （除此以外，使用 setData 设置 this.data.some 也会触发）
      field === this.data.some.field
    }, -->

  <!-- 特别地，仅使用通配符 ** 可以监听全部 setData 。 -->

  <!-- 注意事项
  数据监听器监听的是 setData 涉及到的数据字段，即使这些数据字段的值没有发生变化，数据监听器依然会被触发。
  如果在数据监听器函数中使用 setData 设置本身监听的数据字段，可能会导致死循环，需要特别留意。
  数据监听器和属性的 observer 相比，数据监听器更强大且通常具有更好的性能。 -->


  <!-- 9、纯数据字段 -->
  <!-- 纯数据字段是一些不用于界面渲染的 data 字段，可以用于提升页面更新性能 -->
  <!-- 某些 data 中的字段（包括 setData 设置的字段）既不会展示在界面上，也不会传递给其他组件，仅仅在当前组件内部使用。 -->
  <!-- 它们将仅仅被记录在 this.data 中，而不参与任何界面渲染过程，这样有助于提升页面更新性能 -->

  <!-- 方法：指定“纯数据字段”的方法是在 Component 构造器的 options 定义段中指定 pureDataPattern 为一个正则表达式，字段名符合这个正则表达式的字段将成为纯数据字段。 -->
  <!-- 注意：属性中的纯数据字段的属性 observer 永远不会触发！如果想要监听属性值变化 -->
  <!-- 模板： 
  options: {
    pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
  } -->

  <!-- 组件属性中的纯数据字段 除了this.data 内部数据可以设置为纯数据字段 也可以把属性设置为纯数据字段 用法相同 正常传入但不参与页面渲染 -->
  <!-- 属性中的纯数据字段可以像普通属性一样接收外部传入的属性值，但不能将它直接用于组件自身的 WXML 中。 -->

  <!-- 从小程序基础库版本 2.10.1 开始，也可以在页面或自定义组件的 json 文件中配置 pureDataPattern （这样就不需在 js 文件的 options 中再配置） -->

  <!-- 10、抽象节点 -->
  <!-- 其中，“selectable”不是任何在 json 文件的 usingComponents 字段中声明的组件，而是一个抽象节点。它需要在 componentGenerics 字段中声明 -->
  <!-- {
    "componentGenerics": {
      "selectable": true
    }
  } -->

  <!-- 使用包含抽象节点的组件 -->
  <!-- 在使用 selectable-group 组件时，必须指定“selectable”具体是哪个组件： -->
  <!-- 模板：<selectable-group generic:selectable="custom-radio" /> -->

  <!-- 抽象节点的默认组件 -->
  <!-- 抽象节点可以指定一个默认组件，当具体组件未被指定时，将创建默认组件的实例。默认组件可以在 componentGenerics 字段中指定 -->
  <!-- 模板：
  {
    "componentGenerics": {
      "selectable": {
        "default": "path/to/default/component"
      }
    }
  } -->

  <!-- 注意事项
  节点的 generic 引用 generic:xxx="yyy" 中，值 yyy 只能是静态值，不能包含数据绑定。因而抽象节点特性并不适用于动态决定节点名的场景。 -->

</body>

</html>